 /*  Drakensang Online Patcher
 *     ~AutoHotkey Script~
 *  ALPHA6:
 *  x - check for INI file at startup, else create INI, read INI settings
 *  x   - if ini-file not found, prompt user to specify paths, save default config (INI)
 *  x - Save Preferences
 *  x - Load Preferences => Update GUI
 *  x - reload foldertree (per contextmenu)
 *  x - reload texturelist(per contextmenu)
 *  x - open file via doubleclick from listview
 *  x - open files via contextmenu from listview
 *  x - selection functionality via filters (checkboxes + searchfield)
 *  x - rip texture files from DSOCLient (rename), MsgBox YES&NO: do NOT copy if already exist in texture pack?
 *  x   - renaming process I: get rid of prefix "export_win32_textures_", get rid of suffix (hash code) after .EXT
 *  x - patch files in DSOClient (universal process suitable for all filetypes, locate equivalent -> rename and overwrite) 
 *  x   - MsgBox with "xxx of yyy files patched successfully!" [BETA: +list of failure-details? or select failures in list]
 *  x - alternative patching using hash codes from "__toc"-files
 *  x - clear: delete textures in cache (DSOClient) for reset (+MsgBox "Are you sure you want to delete...?) / FileRecycle takes too long
 *  x - statusbar (#selected files, total size of selected, absolute path)
 *  ^ - canceled: progressbar in statusbar slows down process tremendously!
 *  x - tooltips
 *  x - Online Help
 *  x - Icon
 *  ? ROADMAP:
 *    - new tab: Rendersettings
 *      - Shadowbuffer resolution
 *      - Rendertarget resolution (relwidth & relhight)
 *  ! ALPHA Issues:
 *      - TextureList mibehaves after reload sometimes (adds redundant items)
 *      - visual response to selection of first folder after reload missing
 *      - 32Bit compiled: picture in aboutwindow doesn't appear
 *    BETA:
 *    - Errorhandling (what if folders/files were deleted in the mean while? no files to rip etc.)
 *    - ConfigWindow etc.
 * #######################################################################
 */

; General Information for title and aboutbox
Author = latchkey_child
Version = v0.6 ALPHA
OSBitness = Win64Bit  ; This is an indicator for the installed AHK version, as 64bit compiled exe won't run on 32bit machines
INIFile = DSO_Patcher.ini

if FileExist(INIFile) ; Read the INI-file at startup, if it exists
    ReadINI(INIFile)  ; read the keys from the file
else ; If INI-file doesn't exist, ask for paths and create it with default preferences
    {
    INIKeys=FolderTreeRoot,PatchDestination,TextureFormat,TexturePrefix,HashFiles,GUIFilter,IconsFilter,CharsFilter,NPCsFilter,EnvFilter,ColorFilter,NormalFilter,SpecFilter,CheckGUI,CheckIcons,CheckChars,CheckNPCs,CheckEnv,CheckColor,CheckNormal,CheckSpec,CustomFilter,RadioSwitch,CheckInstallMode
    ; Set default values
    gosub SetFolderTreeRoot    ; Set value for INIKey and variable FolderTreeRoot
    gosub SetPatchDestination  ; Set value for INIKey and variable PatchDestination
    TextureFormat = dds         ; Set other keys to default values...
    TexturePrefix = export_win32_textures_
    HashFiles = __toc
    GUIFilter = ui_,mousecursor_,npc_portraits_
    IconsFilter = icons_
    CharsFilter = armor_,weapons_
    NPCsFilter = npc_npc_,creatures_
    EnvFilter = t000_,t001_,t002_,t003_,t004_,t005_,t006_,t007_,t008_,t009_,t010_,t011_,t012_,t013_,t014_,t015_,t016_
    ColorFilter = _color
    NormalFilter = _bump
    SpecFilter = _spec
    CheckGUI = 0
    CheckIcons = 0
    CheckChars = 0
    CheckNPCs = 0
    CheckEnv = 0
    CheckColor = 1
    CheckNormal = 1
    CheckSpec = 1
    CustomFilter =
    RadioSwitch = 1
    CheckInstallMode = 1

    WriteINI(INIFile, INIKeys) ; Pass default values to INI-file
    }


#NoTrayIcon
; Create contextmenus
Menu, FolderContextMenu, Add, Reload Folders, UpdateFolderTree
Menu, TextureContextMenu, Add, Open Selected File(s), OpenSelectedFiles
Menu, TextureContextMenu, Add, Open Focused File, OpenFocusedFile
Menu, TextureContextMenu, Add, Reload Textures, UpdateTextureList
Menu, TextureContextMenu, Add, Apply Selection Filters, ApplySelectionFilters
; Create the sub-menus for the menu bar:
Menu, RipMenu, Add, Rip DSO &textures to current package, RipTextures
Menu, PathsMenu, Add, &TexturePackage Root Directory, ModifyFolderTreeRoot
Menu, PathsMenu, Add, Drakensang Online &Client, SetPatchDestination
Menu, Filemenu, Add, &Rip, :RipMenu  ; Attach sub-menu to sub-menu
Menu, FileMenu, Add, Set &Paths, :PathsMenu
Menu, FileMenu, Add, &Load Config, LoadConfig
Menu, FileMenu, Add, &Save Config, SaveConfig
Menu, FileMenu, Add  ; Separator line.
Menu, FileMenu, Add, E&xit, FileExit
Menu, HelpMenu, Add, View &Help Online, LaunchOnlineHelp
Menu, HelpMenu, Add, &About, HelpAbout

; Create the menu bar by attaching the sub-menus to it:
Menu, MenuBar, Add, &File, :FileMenu
Menu, MenuBar, Add, &Help, :HelpMenu

; Attach the menu bar to the window:
GUI, Menu, MenuBar

; Create an ImageList and put some standard system icons into it:
ImageListID := IL_Create(5)
Loop 5  ; Below omits the DLL's path so that it works on Windows 9x too:
    IL_Add(ImageListID, "shell32.dll", A_Index)

; Create the GUI
Gui, Add, Tab2, x2 y2 w800 h560 , Textures||Other
Gui, Tab, Textures
Gui, Add, GroupBox, x12 y32 w190 h520 , Texture Packages
Gui, Add, TreeView, vFolderTree gFolderTree ImageList%ImageListID% x22 y52 w170 h490
Gui, Add, GroupBox, x212 y32 w380 h520 , Texture Files
Gui, Add, ListView, vTextureList gTextureList AltSubmit x222 y52 w360 h490 , Name|Size (KB)|Modified

Gui, Add, GroupBox, x602 y32 w190 h380 , Selection
Gui, Add, Button,   gSelectAll x622 y62 w50 h20 , ALL
Gui, Add, Button,   gSelectNone x720 y62 w50 h20 , NONE
Gui, Add, Button,   gSelectInvert x672 y92 w50 h20 , Invert
Gui, Add, CheckBox, vCheckGUI gCheckGUI Checked%CheckGUI% x612 y122 w40 h20, GUI
Gui, Add, CheckBox, vCheckIcons gCheckIcons Checked%CheckIcons% x632 y147 w50 h20 , Icons
Gui, Add, CheckBox, vCheckChars gCheckChars Checked%CheckChars% x612 y182 w75 h30 , Characters
Gui, Add, CheckBox, vCheckNPCs gCheckNPCs Checked%CheckNPCs% x632 y212 w50 h20 , NPCs
Gui, Add, CheckBox, vCheckEnv gCheckEnv Checked%CheckEnv% x612 y242 w90 h30 , Environments
Gui, Add, CheckBox, vCheckColor gCheckColor Checked%CheckColor% x612 y302 w50 h30 , color
Gui, Add, CheckBox, vCheckNormal gCheckNormal Checked%CheckNormal% x662 y302 w55 h30 , normal
Gui, Add, CheckBox, vCheckSpec gCheckSpec Checked%CheckSpec% x722 y302 w60 h30 , specular
Gui, Add, Text,                                           x612 y352         ,Custom:
Gui, Add, Edit,     vCustomFilter gCustomFilter Lowercase x612 y372 w110 h20 -Wrap, %CustomFilter%
Gui, Add, Radio,    vRadioSwitch gRadioSwitch Checked%RadioSwitch% x730 y362 w40 h20 , Add
RadioSwitch := 1-RadioSwitch  ; Invert to set subtract-radiobutton accordingly
Gui, Add, Radio,    vRadioSub gRadioSwitch Checked%RadioSwitch% x730 y382 w60 h20 , Subtract


Gui, Add, GroupBox, x602 y422 w190 h130 , Patch
Gui, Add, Button, vPatchButton gPatchTextures x612 y452 w100 h30 , Patch
Gui, Add, CheckBox, vCheckInstallMode gCheckInstallMode Checked%CheckInstallMode% x730 y452 w60 h30 , Install Mode
Gui, Add, Button, vClearButton gClear x612 y502 w100 h30 , Clear
; Partly generated using SmartGUI Creator 4.0

Gui, Tab, Other
Gui, Font, S14
Gui, Add, Text, Center x80 y250, In future releases more features might be offered like:`npatching language files (quests, names of items, etc.), rendersettings or other.
Gui, Font

; Create a Status Bar to give info about the number of selected files and their total size:
Gui, Add, StatusBar, vStatusBar
SB_SetParts(120, 85)  ; Create three parts in the bar (the third part fills all the remaining width)


; Tooltips
TextureList_TT = right click to show context menu
FolderTree_TT = right click to show context menu
CheckGUI_TT = GUI textures`nfilter: %GuiFilter%
CheckIcons_TT = icons textures`nfilter: %IconsFilter%
CheckChars_TT = character textures`nfilter: %CharsFilter%
CheckNPCs_TT = NPC textures`nfilter: %NPCsFilter%
CheckEnv_TT = environment textures`nfilter: %EnvFilter%
CheckColor_TT = color maps`nfilter: %ColorFilter%
CheckNormal_TT = normal maps`nfilter: %NormalFilter%
CheckSpec_TT = specular maps`nfilter: %SpecFilter%
CustomFilter_TT = seperate by comma (without space)
RadioSwitch_TT = add custom filter to selection
RadioSub_TT = substract custom filter from selection
PatchButton_TT = patch selected files to:`n"%PatchDestination%"
CheckInstallMode_TT = copies also files that hadn't been streamed yet
ClearButton_TT = move all texture files in "%PatchDestination%" to recycle bin


; Add folders and their subfolders to the foldertree. Display the status in case loading takes a long time:
SplashTextOn, 200, 25, Drakensang Online Patcher, Loading folder structure...
AddSubFoldersToTree(FolderTreeRoot)
SplashTextOff

Gui, Show, h587 w802, Drakensang Online Patcher %Version% %OSBitness%
OnMessage(0x200, "WM_MOUSEMOVE")
Return



; Function that writes the parameters and paths to an ini-file
WriteINI(INIFile, INIKeys)
{
    IniWrite, %INIKeys%, %INIFile%, Keys, Keys  ; Write keys=%INIKEY% to section[Keys]
    Loop, parse, INIKeys, `,                        ; Write keys with values to INIFile
    {
        Value := %A_LoopField%  ; whatever A_LoopField contains is assumed to be the name of another variable
        IniWrite, %Value%, %INIFile%, Settings, %A_LoopField%
    }
}


; Function that reads the keys of the INI-file and sets parameters
ReadINI(INIFile)
{
    global INIKeys
    IniRead, INIKeys, %INIFile%, Keys, Keys, Error
    Loop, parse, INIKeys, `,
    {
        IniRead, INIOutput, %INIFile%, Settings, %A_LoopField%
        SetEnv, %A_LoopField%, %INIOutput% ; A_LoopField contains variablename, INIOutput the value, we want to set to that variable
    }
}


; This subroutine saves the current settings and preferences as an ini-file specified by the user
SaveConfig:
    FileSelectFile, INIFileDest, S24 , , Save As, Configuration File (*.ini)
    if ErrorLevel
    return
else
{
    ; automatic .ini extension, when ".ini" omitted
    ; Note: This will overwrite any existing file without a dialog. I'm too lazy to work around that
    IfNotInString, INIFileDest, .ini
        INIFileDest = %INIFileDest%.ini
    WriteINI(INIFileDest, INIKeys)
}
return


; This subroutine loads settings and preferences from an ini-file specified by the user
LoadConfig:
    FileSelectFile, INIFileSrc, 3, , Open Configuration File, Configuration File (*.ini)
    if INIFileSrc =  ; if opening is canceled, do nothing
    return
else
    {
    ReadINI(INIFileSrc)
    gosub UpdateFolderTree
    ; Update GUI
    GuiControl,, CheckGUI, %CheckGUI%
    GuiControl,, CheckIcons, %CheckIcons%
    GuiControl,, CheckChars, %CheckChars%
    GuiControl,, CheckNPCs, %CheckNPCs%
    GuiControl,, CheckEnv, %CheckEnv%
    GuiControl,, CheckColor, %CheckColor%
    GuiControl,, CheckNormal, %CheckNormal%
    GuiControl,, CheckSpec, %CheckSpec%
    GuiControl,, Customfilter, %CustomFilter%
    GuiControl,, RadioSwitch, %RadioSwitch%
    RadioSwitch := 1-RadioSwitch  ; Invert to set subtract-radiobutton accordingly
    GuiControl,, RadioSub, %RadioSwitch%
    GuiControl,, CheckInstallMode, %CheckInstallMode%
    }
return


; This subroutine prompts the user to specify the patch destination path
SetPatchDestination:
FileSelectFolder, PatchDestination, *%A_Temp%\DSOClient, 2, Select the DSOClient directory (patch destination)
if PatchDestination =
    {
    PatchDestination = %A_Temp%\DSOClient
    MsgBox, You didn't select a folder.`n Set to "%PatchDestination%"
    }
return


; This subroutine prompts the user to specify the directory containing texture packs
SetFolderTreeRoot:
FileSelectFolder, FolderTreeRoot, *%A_MyDocuments%, 3, Select folder which contains texture packs `nWARNING: Including many sub-folders slows down startup
if FolderTreeRoot =
    {
    FolderTreeRoot = %A_MyDocuments%
    MsgBox, You didn't select a folder.`n Set to "%FolderTreeRoot%"
    }
return


; This function adds to the TreeView all subfolders in the specified folder
AddSubFoldersToTree(Folder, ParentItemID = 0)
{
    ; It also calls itself recursively to gather nested folders to any depth
    Loop %Folder%\*.*, 2  ; Retrieve all of Folder's sub-folders
        AddSubFoldersToTree(A_LoopFileFullPath, TV_Add(A_LoopFileName, ParentItemID, "Icon4"))
}


; Replace folders in the tree and update the GUI
ModifyFolderTreeRoot:
    gosub SetFolderTreeRoot
    gosub UpdateFolderTree
return


; Build new FolderTree and update the GUI
UpdateFolderTree:
    SplashTextOn, 200, 25, Drakensang Online Patcher, Loading folder structure...
    TV_Delete()
    LV_Delete()
    GuiControl, -Redraw, FolderTree
    AddSubFoldersToTree(FolderTreeRoot)
    GuiControl, +Redraw, FolderTree
    SplashTextOff
    SelectedItemID := TV_GetChild(0)  ; Get the ItemID of the first folder and select it
    ; TODO: visual response to selection in FolderTree?
    TV_GetText(SelectedItemText, SelectedItemID)  ; Get the name of the selected ItemID
    SelectedFullPath = %FolderTreeRoot%\%SelectedItemText%
    gosub UpdateTextureList
return


; This subroutine handles user actions in folder tree(such as clicking)
FolderTree:
if A_GuiEvent <> S  ; i.e. an event other than "select new tree item"
    return  ; Do nothing
; Otherwise, populate the ListView with the contents of the selected folder
; First determine the full path of the selected folder:
TV_GetText(SelectedItemText, A_EventInfo)
ParentID := A_EventInfo
Loop  ; Build the full path to the selected folder
{
    ParentID := TV_GetParent(ParentID)
    if not ParentID  ; No more ancestors
        break
    TV_GetText(ParentText, ParentID)
    SelectedItemText = %ParentText%\%SelectedItemText%
}
SelectedFullPath = %FolderTreeRoot%\%SelectedItemText%
gosub UpdateTextureList
return


; This subroutine reloads the TextureList
UpdateTextureList:
SplashTextOn, 200, 25, Drakensang Online Patcher, Loading texture files...
LV_Delete()  ; Clear all rows.
GuiControl, -Redraw, TextureList  ; Improve performance by disabling redrawing during load
; Put the files into the TextureList:
Loop %SelectedFullPath%\*.%TextureFormat%  ; For simplicity, only texture files are shown in the ListView
{
    LV_Add("", A_LoopFileName, A_LoopFileSizeKB, A_LoopFileTimeModified)
    LV_ModifyCol()  ; Auto-size each column to fit its contents.
    LV_ModifyCol(2, "55 Integer") ; For sorting purposes, indicate that the size column is an integer and make it a little wider to reveal its header
}
GuiControl, +Redraw, TextureList
SplashTextOff
gosub UpdateStatusBar
return


; This subroutine handles user actions in TextureList(such as clicking)
TextureList:
gosub UpdateStatusBar ; UpdateStatusBar by any action
if A_GuiEvent = DoubleClick  ; if the user double-clicked a list item, try to open it
{
    LV_GetText(Filename, A_EventInfo)
    Filename = %SelectedFullPath%\%Filename%
    OpenFile(Filename)
}
return


; Launched in response to a right-click or press of the Apps key
GuiContextMenu:
if A_GuiControl not in FolderTree,TextureList ; Display the menu only for clicks inside the FolderTree or TextureList
    return
if A_GuiControl = FolderTree
    ; Show the menu correctly at the provided coordinates, A_GuiX and A_GuiY, even if the user pressed the Apps key:
    Menu, FolderContextMenu, Show, %A_GuiX%, %A_GuiY%
if A_GuiControl = TextureList
    Menu, TextureContextMenu, Show, %A_GuiX%, %A_GuiY%
return


; Open all the selected files with an external program
OpenSelectedFiles:
SelectedFiles = 0  ; This causes the first loop iteration to start the search at the top of the list
Loop
{
    SelectedFiles := LV_GetNext(SelectedFiles)  ; Resume the search at the row after that found by the previous iteration
    if not SelectedFiles  ; The above returned zero, so there are no more selected rows
        break
    LV_GetText(Filename, SelectedFiles)
    Filename = %SelectedFullPath%\%Filename%  ; Create the absolute path to the file
    OpenFile(Filename)
}
return


; Open only the focused file with an external program
OpenFocusedFile:
    FocusedFile := LV_GetNext(0, "Focused") ; Resume the rownumber of the focused file
    if not FocusedFile
        return
    LV_GetText(Filename, FocusedFile)
    Filename = %SelectedFullPath%\%Filename%  ; Create the absolute path to the file
    OpenFile(Filename)
return


; Simply run a file and resume errormessage if failed
OpenFile(File)
{
   Run, %File%,, UseErrorLevel
   if ErrorLevel = ERROR
     MsgBox Could not open the specified file:`n"%File%". 
}


; Select textures according to filters
ApplySelectionFilters:
LV_Modify(0, "-Select")   ; First deselect all
gosub CheckGUI
gosub CheckIcons
gosub CheckChars
gosub CheckNPCs
gosub CheckEnv
; Only deselect color/normal/specular for convenience
if CheckColor = 0
    gosub CheckColor
if CheckNormal = 0
    gosub CheckNormal
if CheckSpec = 0
    gosub CheckSpec
gosub RadioSwitch
return


; Generate a list containing selected items
GetSelection()
{
;SelectionList =  ; init empty SelectionList //local? no need? starts blank anyway?
Loop  ; Get the current selection in listform
{
    SelectedFiles := LV_GetNext(SelectedFiles)  ; Resume the search at the row after that found by the previous iteration
    if not SelectedFiles  ; The above returned zero, so there are no more selected rows
        break
    LV_GetText(Filename, SelectedFiles, 1)
    SelectionList = %SelectionList%`,%Filename%
}
StringTrimLeft, SelectionList, SelectionList, 1  ; remove first comma
return %SelectionList%
}


; Function that selects items in the list
SelectFiles(Filter, Toggle)
{
Loop % LV_GetCount()
{
    LV_GetText(Filename, A_Index)
    if Filename contains %Filter%
    {
        if Toggle
        LV_Modify(A_Index, "Select")
        else
        LV_Modify(A_Index, "-Select")
    }
}
}



; Following are the GUI-selection operations:
SelectAll:
; Check all selection checkboxes
GuiControl, , CheckGUI, 1
GuiControl, , CheckIcons, 1
GuiControl, , CheckChars, 1
GuiControl, , CheckNPCs, 1
GuiControl, , CheckEnv, 1
GuiControl, , CheckColor, 1
GuiControl, , CheckNormal, 1
GuiControl, , CheckSpec, 1
LV_Modify(0, "Select")   ; Select all.
return

SelectNone:
; Uncheck selection checkboxes
GuiControl, , CheckGUI, 0
GuiControl, , CheckIcons, 0
GuiControl, , CheckChars, 0
GuiControl, , CheckNPCs, 0
GuiControl, , CheckEnv, 0
;~ GuiControl, , CheckColor, 0
;~ GuiControl, , CheckNormal, 0
;~ GuiControl, , CheckSpec, 0
GuiControl,, RadioSwitch, 0
GuiControl,, RadioSub, 1
LV_Modify(0, "-Select")   ; Deselect all.
return

SelectInvert:
Selection := GetSelection()
Loop % LV_GetCount()
{
    LV_GetText(Filename, A_Index)
    if Filename in %Selection%
        LV_Modify(A_Index, "-Select")  ; Deselect row if it was already selected
    if Filename not in %Selection%
        LV_Modify(A_Index, "Select")  ; Select each row which was not selected
}
return

; select/deselect GUI textures
CheckGUI:
GuiControlGet, CheckGUI  ; Checked?
SelectFiles(GUIFilter, CheckGUI)  ; Apply selection filters
return

; select/deselect Icon textures
CheckIcons:
GuiControlGet, CheckIcons
SelectFiles(IconsFilter, CheckIcons)
return

; select/deselect character textures
CheckChars:
GuiControlGet, CheckChars
SelectFiles(CharsFilter, CheckChars)
return

; select/deselect NPC textures
CheckNPCs:
GuiControlGet, CheckNPCs
SelectFiles(NPCsFilter, CheckNPCs)
return

; select/deselect environment textures
CheckEnv:
GuiControlGet, CheckEnv
SelectFiles(EnvFilter, CheckEnv)
return

; select/deselect color textures
CheckColor:
GuiControlGet, CheckColor
SelectFiles(ColorFilter, CheckColor)
return

; select/deselect normal textures
CheckNormal:
GuiControlGet, CheckNormal
SelectFiles(NormalFilter, CheckNormal)
return

; select/deselect specular textures
CheckSpec:
GuiControlGet, CheckSpec
SelectFiles(SpecFilter, CheckSpec)
return

; select/deselect textures via custom filter
CustomFilter:
Input, CustomFilter, V,{Enter}
IfInString, ErrorLevel, EndKey:Enter
gosub RadioSwitch
return

; Add textures containing custom filter to selection
RadioSwitch:
GuiControlGet, CustomFilter
GuiControlGet, RadioSwitch
SelectFiles(CustomFilter,RadioSwitch)
return


; Check status of patch mode
CheckInstallMode:
GuiControlGet, CheckInstallMode
return



; Copy all the textures from PatchDestination to SelectedFullPath
RipTextures:
MsgBox, 259, Rip Drakensang Online Textures, Overwrite existing files in `n"%SelectedFullPath%"?
IfMsgBox Yes
    Overwrite = 1
IfMsgBox No
    Overwrite = 0
IfMsgBox Cancel
    return
else
CopyCount = 0  ; Init number of copied files
SplashTextOn, 200, 25, Drakensang Online Patcher, ripping files...
Loop, %PatchDestination%\*.%Textureformat%*  ; all files with the extension .%TextureFormat%
{
    SplitPath, A_LoopFileName, , , ,Filename                     ; Remove the cryptic extension
    StringTrimLeft, Filename, Filename, StrLen(TexturePrefix)    ; Remove the prefix string
    FileCopy, %A_LoopFileFullPath%, %SelectedFullPath%\%Filename%, %Overwrite%   ; Copy with overwrite either 0 or 1
    CopyCount += 1-Errorlevel
}
SplashTextOff
gosub UpdateTextureList
MsgBox, 48, Rip process, %CopyCount% files ripped successfully!
return


PatchTextures:
    if CheckInstallMode
        Patch2(TexturePrefix, HashFiles)
    else
        Patch(TexturePrefix, TextureFormat)
return


; The actual patch-function
; Note: With this method only existing files can be patched.
;       The "__toc.*" files contain all the filenames with hashcodes.
;       Could look up the hashcode for each file, which must be part of the filename to work
;       the hash code is the version control
Patch(Prefix, Filetype)
{
global
local CopyCount = 0 ; Init number of copied files
local Selection := GetSelection()
local SelectCount := FileCount  ; FileCount could be altered during process
SplashTextOn, 200, 25, Drakensang Online Patcher, patching only existing files...
Loop, %PatchDestination%\*.%Filetype%*  ; all files with the extension in Filetype
{
    SplitPath, A_LoopFileName, , , ,Filename                     ; Remove the hash code
    StringTrimLeft, Filename, Filename, StrLen(Prefix)    ; Remove the prefix string to find corresponding file in selection
    if Filename in %Selection%
    {
        FileCopy, %SelectedFullPath%\%Filename%, %A_LoopFileFullPath%, 1   ; Copy with overwrite on
        CopyCount += 1  ; Number of copied files
    }
}
SplashTextOff
MsgBox, 48, Patch process, %CopyCount% of %SelectCount% files patched successfully!
}


; The alternative patch-function
; Note: This function looks up the hash codes in the "*__toc.*"-files
;       and appends those to the filename of the package files while copying
;       Hence this is more of an installation, since files can be copied that hadn't been streamed yet (with uptodate hash files!)
Patch2(Prefix, HashFiles)
{
global
local CopyCount = 0 ; Init number of copied files
local Selection := GetSelection()
local SelectCount := FileCount  ; FileCount could be altered during process
SplashTextOn, 200, 25, Drakensang Online Patcher, installing files...
Loop %PatchDestination%\%Prefix%*%HashFiles%*  ; only read the filetypes containing hash codes and only relevant resource (prefix)
{
    Loop, Read, %A_LoopFileFullPath%
    {
        StringReplace, DestFile, A_LoopReadLine, /, _, All  ; Construct the destination filename
        StringReplace, DestFile, DestFile, |f|, ., All
        StringTrimRight, DestFile, DestFile, 2  ; Last 2 characters don't match real filename; these are always "00" ->appended in FileCopy
        StringTrimLeft, Filename, DestFile, StrLen(Prefix)  ; Generate possible filename in selection
        StringTrimRight, Filename, Filename, 31  ; Remove the rest of the ".hash code" length of hash code = 32-2 (2 removed already, see above)
        if Filename in %Selection%
        {
            FileDelete, %PatchDestination%\%Prefix%%Filename%.*  ; first delete old file that possibly has other hashcode
            FileCopy, %SelectedFullPath%\%Filename%, %PatchDestination%\%DestFile%00, 1   ; append "00" to complete destination filename, copy
            CopyCount += 1  ; Number of copied files
        }
    }
}
SplashTextOff
MsgBox, 48, Patch process, %CopyCount% of %SelectCount% files successfully installed!
}


; Deletes the texturefiles in the PathDestination-folder, namely in DSOClient by default
Clear:
MsgBox, 257, Delete Drakensang Online Textures, Are you sure you want to delete Drakensang Online textures in`n"%PatchDestination%"?`nNote: Use this if you want to reset the game's textures.
IfMsgBox Cancel
    return
else
    FileDelete, %PatchDestination%\*.%Textureformat%*  ; all files with the extension .%TextureFormat%
return


; Updates the displayed text in the statusbar
UpdateStatusBar:
FileCount := LV_GetCount("Selected")
TotalSize := 0 ; init before below
Loop
{
    SelectedFiles := LV_GetNext(SelectedFiles)  ; Resume the search at the row after that found by the previous iteration
    if not SelectedFiles  ; The above returned zero, so there are no more selected rows
        break
    LV_GetText(Filesize, SelectedFiles, 2)
    TotalSize += Filesize
}
; Update the three parts of the status bar to show info about the currently selected files:
SB_SetText(FileCount . " files selected", 1)
SB_SetText(TotalSize . " KB", 2)
SB_SetText(SelectedFullPath, 3)
return



; This function is enabled by movement of the mouse and displays the tooltips
WM_MOUSEMOVE()
{
    static CurrControl, PrevControl, _TT  ; _TT is kept blank for use by the ToolTip command below
    CurrControl := A_GuiControl  ; Get current control under the mouse
    If (CurrControl <> PrevControl and not InStr(CurrControl, " "))  ; When other than previous control and not nothing
    {
        ToolTip  ; Turn off any previous tooltip by leaving TEXT blank
        SetTimer, DisplayToolTip, 1000
        PrevControl := CurrControl  ; Pass ControlID to PrevControl to display tooltip only once while mouse hovers over the control
    }
    return

    ; Turns off a previous set timer that called the process, displays a dynamic tooltip and initializes removal
    DisplayToolTip:
    SetTimer, DisplayToolTip, Off  ; Disables the timer
    ToolTip % %CurrControl%_TT  ; Display tooltip corresponding to ControlID, the leading percent sign tells it to use an expression
    SetTimer, RemoveToolTip, 3000
    return

    ; Disables the timer and kills any existing tooltip
    RemoveToolTip:
    SetTimer, RemoveToolTip, Off
    ToolTip
    return
}


; The about box from the help menu
HelpAbout:
Gui, 2:+owner1  ; Make the main window (Gui #1) the owner of the "about box" (Gui #2)
Gui +Disabled  ; Disable main window
Gui, 2:Color, FFFFFF
Gui, 2:Add, Picture, x390 w192 h-1, DSO_Patcher_ICON.ico  ; TODO: embed in compiled version
Gui, 2:Font, S24
Gui, 2:Add, Text, xm ym+20 Center, Drakensang Online Patcher
Gui, 2:Font, S14
Gui, 2:Add, Text, , %Version% %OSBitness%`nby %Author%
Gui, 2:Font, underline
Gui, 2:Add, Text, cBlue vLaunchProjectWebsiteText gLaunchProjectWebsite, http://dso-patcher.googlecode.com
Gui, 2:Add, Text, cBlue vLaunchDSOText gLaunchDSO, www.drakensang-online.com
Gui, 2:Font, norm
Gui, 2:Font
Gui, 2:Add, Button, x491 y189 w87 h26 Default, OK
Gui, 2:Show, w586 h222, About
return

LaunchOnlineHelp:
Run http://code.google.com/p/dso-patcher/wiki/Help
return

LaunchProjectWebsite:
Run http://dso-patcher.googlecode.com
return

LaunchDSO:
Run www.drakensang-online.com
return

2ButtonOK:  ; This section is used by the "about box" above
2GuiClose:
2GuiEscape:
Gui, 1:-Disabled  ; Re-enable the main window (must be done prior to destroying the window)
Gui Destroy  ; Destroy the about box
return


FileExit:     ; User chose "Exit" from the File menu.
GuiClose:  ; User closed the window.
ExitApp
